package org.windowkit.toolkit.widget.tree;

import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;


/**
 *
 * @author Eric Lundin
 */
public class TreePane extends JComponent {

	/**
	 *
	 */
	private static final long serialVersionUID = -5671471240196841367L;

	// The text/action command for expanding the entire tree from a popup menu.
	private static final String EXPAND_ENTIRE_TREE = "Expand Tree";

	// The text/action command for collapsing the entire tree from a popup menu.
	private static final String COLLAPSE_ENTIRE_TREE = "Collapse Tree";

	// The text/action command for printing the tree from a popup menu.
	private static final String PRINT_TREE = "Print Tree";

	// Defines the space between the edge of the tree and the scrollpane.
	private static final int TOP_MARGIN = 5;

	// Defines the space between the edge of the tree and the scrollpane.
	private static final int LEFT_MARGIN = 5;

	// Defines the space between the edge of the tree and the scrollpane.
	private static final int BOTTOM_MARGIN = 0;

	// Defines the space between the edge of the tree and the scrollpane.
	private static final int RIGHT_MARGIN = 0;

	// The model to back the view with.
	BaseTreeModel model;

	// The scroll pane to display this tree.
	JScrollPane scrollPane;

	// The tree we will use as the view.
	Tree theTree;

	// The popup menu for this tree.
	JPopupMenu menu;

	// The action listener for the popup menu
	private ActionListener sTreePaneMenuActionListener;

	// The mouse listener for the popup menu
	private TreePaneMouseListener sTreePaneMouseListener;

	/**
	 * Creates a tree with a null/nonexistant root node.
	 */
	public TreePane() {
		this(null);
	}

	/**
	 * Creates a tree with the specified root node.
	 *
	 * @param root
	 *            The root node for this tree.
	 * @param componentID
	 *            Unique component identifier.
	 * @see SDefaultTreeNode
	 */
	public TreePane(final TreeNode root) {
		init(root);
	}

	/**
	 * Initialize the parts of this component
	 *
	 * @param root
	 */
	private void init(final TreeNode root) {
		initThisComponent(initTree(initModel(root)));
		addListeners();
		setPopupMenu(null);
	}

	/**
	 * Returns the tree held by this tree pane.
	 *
	 * @return The tree held by this tree pane.
	 */
	Tree getTree() {
		// TODO REMOVE ME, PUT DRAG AND DROP INTO THIS CLASS IF WE STILL WANT
		// IT.
		return theTree;
	}

	/**
	 * Initializes the tree model.
	 *
	 * @return The initialized tree model.
	 */
	private TreeModel initModel(final TreeNode root) {
		model = new BaseTreeModel(root);
		return model;
	}

	/**
	 * Initializes this component by adding the tree to the scrollpane, adding
	 * the scrollpane to this component, setting the border of the tree etc.
	 *
	 * @param theTree
	 *            The tree to display.
	 */
	private void initThisComponent(final JTree theTree) {
		scrollPane = new JScrollPane(theTree);
		scrollPane
				.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		scrollPane
				.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
		setLayout(new GridLayout());
		add(scrollPane);

		// This keeps the edge of the tree from being right up against the edge
		// of the viewport for readability.
		theTree.setBorder(BorderFactory.createEmptyBorder(TOP_MARGIN,
				LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN));
	}

	/**
	 * Initializes the tree.
	 *
	 * @param model
	 *            The tree model to initialize the tree with.
	 * @return The initialized tree.
	 */
	private JTree initTree(final TreeModel model) {
		theTree = new Tree(model);
		return theTree;
	}

	/**
	 * Returns whether the tree is currently editable.
	 *
	 * @return True if the tree is editable, false otherwise.
	 */
	public boolean isEditable() {
		return theTree.isEditable();
	}

	/**
	 * Adds a branch represented by the given tree node to the root node.
	 *
	 * @param branchNode
	 *            The branch to be added.
	 */
	public void addNode(final MutableTreeNode branchNode) {
		TreeNode parent = branchNode.getParent() == null ? (TreeNode) model
				.getRoot() : branchNode.getParent();
		model.insertNodeInto(branchNode,
				(MutableTreeNode) parent, parent.getChildCount());
	}

	// /**
	// * Adds a branch repreented by the given tree node to the specified node.
	// If
	// * no node
	// *
	// * @param branchNode
	// * The branch to be added.
	// */
	// public void addNode(MutableTreeNode branchNode, MutableTreeNode parent) {
	// if (parent == null)
	// parent = (MutableTreeNode) model.getRoot();
	// branchNode.setParent(parent);
	// model.insertNodeInto((MutableTreeNode) branchNode,
	// (MutableTreeNode) parent, parent.getChildCount());
	// }

	/**
	 * Scale the tree for printing, display a print dialog, print to the
	 * printer.
	 */
	public void printTree() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				TreePrinter printer = new TreePrinter();
				PrinterJob printJob = PrinterJob.getPrinterJob();
				printJob.setPrintable(printer);
				// TODO Make me scale based on tree size!

				if (printJob.printDialog()) {
					try {
						printJob.print();
					} catch (PrinterException e) {

					}
				}
				else {
					printJob.cancel();
				}
			}
		});
	}

	/**
	 * Adds listeners to various parts of this component.
	 */
	private void addListeners() {
		// TODO: Figure out who will listen for what
		sTreePaneMenuActionListener = new TreePaneMenuActionListener();
		sTreePaneMouseListener = new TreePaneMouseListener();
		theTree.addMouseListener(sTreePaneMouseListener);
		theTree.addTreeExpansionListener(new TreePaneExpansionListener());
		theTree.addTreeSelectionListener(new TreePaneSelectionListener());
		theTree.addTreeWillExpandListener(new TreePaneWillExpandListener());
		model.addTreeModelListener(new MyTreeModelListener());
	}

	/**
	 * Retrieve the nodes which are currently selected by the user.
	 *
	 * @return An array containing the nodes selected by the user.
	 */
	public BaseDefaultMutableTreeNode[] getSelectedNodes() {
		BaseDefaultMutableTreeNode[] selectedNodes;
		TreePath[] selectedNodePaths = theTree.getSelectionModel()
				.getSelectionPaths();
		if (selectedNodePaths == null) {
			return null;
		}
		selectedNodes = new BaseDefaultMutableTreeNode[selectedNodePaths.length];
		for (int i = 0; i < selectedNodePaths.length; i++) {
			selectedNodes[i] = (BaseDefaultMutableTreeNode) selectedNodePaths[i]
					.getLastPathComponent();
		}
		return selectedNodes;
	}

	/**
	 * Sets if the tree is editable.
	 *
	 * @param edit
	 *            True if the tree is editable, false otherwsie.
	 */
	public void setEditable(final boolean edit) {
		theTree.setEditable(edit);
	}

	/**
	 * Set whether the given node should be expanded or collapsed.
	 *
	 * @param node
	 *            The node to expand/collapse.
	 * @param expanded
	 *            Whether the node should be exapnded.
	 */
	public void setNodeExpanded(final MutableTreeNode node, final boolean expanded) {
		if (expanded) {
			theTree.expandPath(new TreePath(node));
		} else {
			theTree.collapsePath(new TreePath(node));
		}
	}

	/**
	 * Sets the size of the row based on the size of the icon.
	 */
	private void sizeRowsByIcons() {
		int largestIconSize = 0;
		DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) theTree
				.getCellRenderer();
		largestIconSize = Math.max(Math.max(renderer.getOpenIcon()
				.getIconHeight(), renderer.getClosedIcon().getIconHeight()),
				renderer.getLeafIcon().getIconHeight());
		theTree.setRowHeight(largestIconSize);
	}

	/**
	 * Set the icon to display on leaf nodes. The icon can be a traditional
	 * painted icon or an ImageIcon. The row height will be set to allow the
	 * entire icon to be displayed. It is recommended that all icons are the
	 * same size as the row height will be set to accommodate the largest icon.
	 *
	 * @param leafIcon
	 *            The icon to use for leaf nodes.
	 */
	public void setLeafIcon(final Icon leafIcon) {
		DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) theTree
				.getCellRenderer();
		renderer.setLeafIcon(leafIcon);
		sizeRowsByIcons();
	}

	/**
	 * Set the icon to display on closed nodes. The icon can be a traditional
	 * painted icon or an ImageIcon. The row height will be set to allow the
	 * entire icon to be displayed. It is recommended that all icons are the
	 * same size as the row height will be set to accommodate the largest icon.
	 *
	 * @param closedIcon
	 *            The icon to use for open nodes.
	 */
	public void setClosedIcon(final Icon closedIcon) {
		DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) theTree
				.getCellRenderer();
		renderer.setClosedIcon(closedIcon);
		sizeRowsByIcons();
	}

	/**
	 * Sets whether or not to allow dragging on this tree. Commented out may be
	 * added later
	 *
	 * @param allowDrag
	 *            True to allow dragging false otherwise.
	 */
	protected void setDragEnabled(final boolean allowDrag) {
		// theTree.setDragEnabled(allowDrag);
	}

	/**
	 * Set the icon to display on open nodes. The icon can be a traditional
	 * painted icon or an ImageIcon. The row height will be set to allow the
	 * entire icon to be displayed. It is recommended that all icons are the
	 * same size as the row height will be set to accommodate the largest icon.
	 *
	 * @param openIcon
	 *            The icon to use for open nodes.
	 */
	public void setOpenIcon(final Icon openIcon) {
		DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) theTree
				.getCellRenderer();
		renderer.setOpenIcon(openIcon);
		sizeRowsByIcons();
	}

	/**
	 * Removes the assigned icon to display on leaf nodes. The default leaf node
	 * for the look and feel will be used.
	 */
	public void removeLeafIcon() {
		DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) theTree
				.getCellRenderer();
		renderer.setLeafIcon(null);
		sizeRowsByIcons();
	}

	/**
	 * Removes the assigned icon to display on closed nodes. The default closed
	 * node for the look and feel will be used.
	 */
	public void removeClosedIcon() {
		DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) theTree
				.getCellRenderer();
		renderer.setClosedIcon(null);
		sizeRowsByIcons();
	}

	/**
	 * Removes the assigned icon to display on open nodes. The default open node
	 * icon for the look and feel will be used.
	 */
	public void removeOpenIcon() {
		DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) theTree
				.getCellRenderer();
		renderer.setOpenIcon(null);
		sizeRowsByIcons();
	}

	/**
	 * Convienience method to remove all icons and instruct the table to use the
	 * default icons.
	 */
	public void removeAllIcons() {
		removeLeafIcon();
		removeOpenIcon();
		removeClosedIcon();
	}

	/**
	 * Removes this node and all nodes below it.
	 *
	 * @param nodeToRemove
	 * @throws IllegalArgumentException
	 *             If root node is removed.
	 */
	public void removeNode(final MutableTreeNode nodeToRemove) {
		if (nodeToRemove.equals(model.getRoot())) {
			throw new IllegalStateException("Cannot remove root node.");
		}
		model.removeNodeFromParent(nodeToRemove);
	}

	/**
	 * Adds the given branch to the tree.
	 *
	 * @param branch
	 *            The branch to add.
	 * @param parentNode
	 *            The parent to this branch.
	 */
	public void addBranch(final BaseDefaultMutableTreeNode branch,
			final BaseDefaultMutableTreeNode parentNode) {
		model.insertNodeInto(branch, parentNode, parentNode.getChildCount());
	}

	/**
	 * Sets the popup menu to be displayed when the user presses the popup
	 * trigger. Appends the default popup menu item to the given menu.
	 *
	 * @param userMenu
	 *            The new popup menu.
	 */
	public void setPopupMenu(final JPopupMenu userMenu) {
		if (userMenu == null) {
			menu = new JPopupMenu();
		} else {
			menu = userMenu;
		}
		appendPopupMenuItems();
	}

	/**
	 * Append the expand/collapse all options to the popup menu.
	 */
	private void appendPopupMenuItems() {
		JMenuItem currentItem;
		if (menu.getSubElements().length > 0) {
			menu.addSeparator();
		}

		currentItem = new JMenuItem(EXPAND_ENTIRE_TREE);
		currentItem.addActionListener(sTreePaneMenuActionListener);
		menu.add(currentItem);

		currentItem = new JMenuItem(COLLAPSE_ENTIRE_TREE);
		currentItem.addActionListener(sTreePaneMenuActionListener);
		menu.add(currentItem);

		currentItem = new JMenuItem(PRINT_TREE);
		currentItem.addActionListener(sTreePaneMenuActionListener);
		menu.add(currentItem);
	}

	/**
	 * Sets whether to display the root node.
	 */
	public void setRootVisible(final boolean visible) {
		theTree.setRootVisible(visible);
	}

	/**
	 * Set the mode of selection allowed on the table. Use:
	 * DefaultTreeSelectionModel.CONTIGUOUS_TREE_SELECTION
	 * DefaultTreeSelectionModel.SINGLE_TREE_SELECTION
	 * DefaultTreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION If none of the
	 * above are used DISCONTIGUOUS_TREE_SELECTION will be used.
	 *
	 * @param mode
	 *            The selection mode for the tree.
	 */
	public void setSelectionMode(final int mode) {
		theTree.getSelectionModel().setSelectionMode(mode);
	}

	/**
	 * Set the current tree selection to be those nodes passed in.
	 *
	 * @param nodes
	 *            The nodes to select.
	 */
	public void setSelectedNodes(final BaseDefaultMutableTreeNode... nodes) {
		TreeSelectionModel selmodel = theTree.getSelectionModel();
		selmodel.clearSelection();
		TreePath[] paths = new TreePath[nodes.length];
		for (int loop = 0; loop < nodes.length; loop++) {
			paths[loop] = new TreePath(nodes[loop].getPath());
		}
		selmodel.setSelectionPaths(paths);
	}

	/**
	 * Set the node who is represented by this path as selected.
	 *
	 * @param path
	 *            The path which represents the node to be selected.
	 */
	public void setSelectionPath(final TreePath path) {
		theTree.setSelectionPath(path);
	}

	/**
	 * Set the nodes who are represented by this path as selected.
	 *
	 * @param path
	 *            The paths which represents the node to be selected.
	 */
	public void setSelectionPaths(final TreePath[] paths) {
		theTree.setSelectionPaths(paths);
	}

	/**
	 * Set whether this component is enabled
	 */
	@Override
	public void setEnabled(final boolean isEnabled) {
		super.setEnabled(isEnabled);
		theTree.setEnabled(isEnabled);
	}

	/**
	 * Get the tree path to the first selected node.
	 *
	 * @return The tree path to the first selected node.
	 */
	public TreePath getSelectionPath() {
		return theTree.getSelectionPath();
	}

	/**
	 * Get the TreePaths to all selected nodes.
	 *
	 * @return All selected paths.
	 */
	public TreePath[] getSelectionPaths() {
		return theTree.getSelectionPaths();
	}

//	/**
//	 * Shares this TreeField's model with another TreeField.
//	 *
//	 * @param otherTree
//	 *            The TreeField to share the model with.
//	 */
//	public void shareModel(TreeField otherTree) {
//		Component currentComponent = otherTree.getComponent(0);
//		while (!(currentComponent instanceof JTree)) {
//			currentComponent = ((Container) currentComponent).getComponent(0);
//		}
//		((JTree) currentComponent).setModel(model);
//	}

	/**
	 * Sets the given row as the selected row.
	 *
	 * @param row
	 *            The row to select.
	 */
	public void setSelectionRow(final int row) {
		theTree.setSelectionRow(row);
	}

	/**
	 * Sets the given rows as the selected rows.
	 *
	 * @param rows
	 *            The rows to select.
	 */
	public void setSelectionRows(final int[] rows) {
		theTree.setSelectionRows(rows);
	}

	/**
	 * Appends the deafult popupmenu items to the end of the developer created
	 * items. If the developer's popup menu as items we will add a seperator
	 * between the developers items and the default items.
	 */
	private void appendDefaultPopupItems() {
		JMenuItem currentItem;
		if (menu.getSubElements().length > 0) {
			menu.addSeparator();
		}

		currentItem = new JMenuItem(EXPAND_ENTIRE_TREE);
		currentItem.addActionListener(sTreePaneMenuActionListener);
		menu.add(currentItem);

		currentItem = new JMenuItem(COLLAPSE_ENTIRE_TREE);
		currentItem.addActionListener(sTreePaneMenuActionListener);
		menu.add(currentItem);

		currentItem = new JMenuItem(PRINT_TREE);
		currentItem.addActionListener(sTreePaneMenuActionListener);
		menu.add(currentItem);
	}

	/**
	 * Ensures that the given node is visible. That is the tree is expanded in
	 * such a way that this node is able to be seen.
	 *
	 * @param theNode
	 *            The node to display.
	 */
	public void makeNodeVisible(final DefaultMutableTreeNode theNode) {
		TreePath thePath = new TreePath(theNode.getPath());
		makeNodeVisible(thePath);
	}

	/**
	 * Ensures that the node represented by the given path is visible. That is
	 * the tree is expanded in such a way that this node is able to be seen.
	 *
	 * @param pathToNode
	 *            The TreePath to the node to display.
	 */
	public void makeNodeVisible(final TreePath pathToNode) {
		theTree.makeVisible(pathToNode);
	}

	public BaseDefaultMutableTreeNode findNodeByDisplayValue(final String value) {
		return findNodeHelper((BaseDefaultMutableTreeNode) getModel().getRoot(),
				value);
	}

	private BaseDefaultMutableTreeNode findNodeHelper(
			final BaseDefaultMutableTreeNode current, final String displayValue) {
		BaseDefaultMutableTreeNode temp;
		if (current.getDisplayValue().equalsIgnoreCase(displayValue)) {
			return current;
		}
		else {
			for (int i = 0; i < current.getChildCount(); i++) {
				if ((temp = findNodeHelper((BaseDefaultMutableTreeNode) current
						.getChildAt(i), displayValue)) != null) {
					return temp;
				}
			}
		}
		return null;
		// return null;
	}

	/**
	 * Clears all branches under the root node. The root node will not be
	 * cleared.
	 */
	public void clear() {
		((DefaultMutableTreeNode) model.getRoot()).removeAllChildren();
		model.reload();
	}

	/**
	 * Determines whether the tree contains the given node.
	 *
	 * @param node
	 *            The node to look for.
	 * @return True if the node exists in this tree. False otherwise.
	 */
	public boolean contains(final DefaultMutableTreeNode node) {
		return theTree.getRowForPath(new TreePath(node.getPath())) == -1 ? false
				: true;
	}

	/**
	 * Expands or collapses all nodes of the given tree depending on the value
	 * passed in.
	 *
	 * @param expand
	 *            True to expand all nodes. False to collapse all nodes.
	 */
	public void expandCollapseEntireTree(final boolean expand) {
		if (expand) {
			for (int i = 0; i < theTree.getRowCount(); i++) {
				theTree.expandRow(i);
			}
		} else {
			for (int i = 0; i < theTree.getRowCount(); i++) {
				theTree.collapseRow(i);
			}
		}
	}

	/**
	 * Selects/Deselects the given node if found in the tree.
	 *
	 * @param theNode
	 *            The node to select/deselect in the tree.
	 * @param select
	 *            True to make this node selected, false to deselect it.
	 */
	public void setNodeSelected(final DefaultMutableTreeNode theNode, final boolean select) {
		TreePath pathToNode = new TreePath(theNode.getPath());
		setNodeSelected(pathToNode, select);
	}

	/**
	 * Selects/Deselects the given node if found in the tree based on the tree
	 * path.
	 *
	 * @param pathToNode
	 *            The path to node to select/deselect in the tree.
	 * @param select
	 *            True to make this node selected, false to deselect it.
	 */
	private void setNodeSelected(final TreePath pathToNode, final boolean select) {
		TreeSelectionModel selModel = theTree.getSelectionModel();
		if (select) {
			selModel.addSelectionPath(pathToNode);
		}
		else {
			selModel.removeSelectionPath(pathToNode);
		}
	}

	/**
	 * Switches the current selection state of this node. That is if the node is
	 * currently selected, deselect it, else select it.
	 *
	 * @param theNode
	 *            The node to toggle the selection state of.
	 */
	public void toggleNodeSelected(final DefaultMutableTreeNode theNode) {
		setNodeSelected(theNode, theTree.getSelectionModel().isPathSelected(
				new TreePath(theNode)));
	}

	/**
	 * Adds a tree expansion listener.
	 *
	 * @param tel
	 *            The tree expansion listener.
	 */
	public void addTreeExpansionListener(final TreeExpansionListener tel) {
		theTree.addTreeExpansionListener(tel);
	}

	/**
	 * Adds a tree will expand listener.
	 *
	 * @param tel
	 *            The tree will expand listener.
	 */
	public void addTreeWillExpandListener(final TreeWillExpandListener twel) {
		theTree.addTreeWillExpandListener(twel);
	}

	/**
	 * Adds a tree selection listener.
	 *
	 * @param tel
	 *            The tree selection listener.
	 */
	public void addTreeSelectionListener(final TreeSelectionListener tsl) {
		theTree.addTreeSelectionListener(tsl);
	}

	/**
	 * Removes a tree expansion listener.
	 *
	 * @param tel
	 *            The tree expansion listener to remove.
	 */
	public void removeTreeExpansionListener(final TreeExpansionListener tel) {
		theTree.removeTreeExpansionListener(tel);
	}

	/**
	 * Removes a tree will expand listener.
	 *
	 * @param tel
	 *            The tree will expand listener to remove.
	 */
	public void removeTreeWillExpandListener(final TreeWillExpandListener twel) {
		theTree.removeTreeWillExpandListener(twel);
	}

	/**
	 * Removes a tree selection listener.
	 *
	 * @param tel
	 *            The tree selection listener to remove.
	 */
	public void removeTreeSelectionListener(final TreeSelectionListener tsl) {
		theTree.removeTreeSelectionListener(tsl);
	}

	private class TreePaneExpansionListener implements TreeExpansionListener {

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.event.TreeExpansionListener#treeExpanded(javax.swing.event.TreeExpansionEvent)
		 */
		public void treeExpanded(final TreeExpansionEvent event) {
			// TODO treeExpanded

		}

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.event.TreeExpansionListener#treeCollapsed(javax.swing.event.TreeExpansionEvent)
		 */
		public void treeCollapsed(final TreeExpansionEvent event) {
			// TODO treeCollapsed

		}

	}

	private class TreePaneWillExpandListener implements
			TreeWillExpandListener {
		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.event.TreeWillExpandListener#treeWillExpand(javax.swing.event.TreeExpansionEvent)
		 */
		public void treeWillExpand(final TreeExpansionEvent event)
				throws ExpandVetoException {
			// TODO treeWillExpand

		}

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.event.TreeWillExpandListener#treeWillCollapse(javax.swing.event.TreeExpansionEvent)
		 */
		public void treeWillCollapse(final TreeExpansionEvent event)
				throws ExpandVetoException {
			// TODO treeWillCollapse

		}

	}

	private class TreePaneSelectionListener implements TreeSelectionListener {

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event.TreeSelectionEvent)
		 */
		public void valueChanged(final TreeSelectionEvent e) {
			// TODO treeValueChanged

		}

	}

	/**
	 * This class listens for mouse events and shows the popup menu if the
	 * correct situation is presented.
	 *
	 * @author Eric Lundin
	 */
	private class TreePaneMouseListener implements MouseListener {

		/*
		 * (non-Javadoc)
		 *
		 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
		 */
		public void mouseClicked(final MouseEvent e) {
			checkShowMenu(e);
		}

		/*
		 * (non-Javadoc)
		 *
		 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
		 */
		public void mousePressed(final MouseEvent e) {
			checkShowMenu(e);

		}

		/*
		 * (non-Javadoc)
		 *
		 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
		 */
		public void mouseReleased(final MouseEvent e) {
			checkShowMenu(e);
		}

		/**
		 * Check to see if the popup menu associated to this table should be
		 * shown. If so show the menu.
		 *
		 * @param e
		 *            The mouse event which may cause the popup menu to be
		 *            shown.
		 */
		private void checkShowMenu(final MouseEvent e) {
			if (e.isPopupTrigger() && menu != null) {
				menu.show(e.getComponent(), e.getX(), e.getY());
			}
		}

		/*
		 * (non-Javadoc)
		 *
		 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
		 */
		public void mouseEntered(final MouseEvent e) {
			// TODO Auto-generated method stub

		}

		/*
		 * (non-Javadoc)
		 *
		 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
		 */
		public void mouseExited(final MouseEvent e) {
			// TODO Auto-generated method stub

		}
	}

	/**
	 * This class handles the actions performed on the popup menu for the
	 * appended items.
	 *
	 * @author Eric Lundin
	 */
	private class TreePaneMenuActionListener implements ActionListener {
		/*
		 * (non-Javadoc)
		 *
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		/**
		 * Determine which menu item was selected and call the appropriate
		 * method.
		 */
		public void actionPerformed(final ActionEvent e) {
			String actionCommand = e.getActionCommand();
			if (actionCommand.equals(EXPAND_ENTIRE_TREE)) {
				expandCollapseEntireTree(true);
			} else if (actionCommand.equals(COLLAPSE_ENTIRE_TREE)) {
				expandCollapseEntireTree(false);
			} else if (actionCommand.equals(PRINT_TREE)) {
				printTree();
			}
		}
	}

	/**
	 * This class handles printing the tree with a print dialog.
	 *
	 * @author Eric Lundin
	 */
	private class TreePrinter implements Printable {
		public int print(final Graphics graphics, final PageFormat pageFormat, final int pageIndex)
				throws PrinterException {
			if (pageIndex > 0) {
				return Printable.NO_SUCH_PAGE;
			}
			else {
				graphics.translate((int) pageFormat.getImageableX(),
						(int) pageFormat.getImageableY());
				theTree.paint(graphics);
				return Printable.PAGE_EXISTS;
			}
		}
	}

	/**
	 * @return
	 */
	protected BaseTreeModel getModel() {
		return (BaseTreeModel) theTree.getModel();
	}

	private class MyTreeModelListener implements TreeModelListener {

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.event.TreeModelListener#treeNodesChanged(javax.swing.event.TreeModelEvent)
		 */
		public void treeNodesChanged(final TreeModelEvent e) {
			// TODO Auto-generated method stub

		}

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.event.TreeModelListener#treeNodesInserted(javax.swing.event.TreeModelEvent)
		 */
		public void treeNodesInserted(final TreeModelEvent e) {
			// TODO Auto-generated method stub

		}

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.event.TreeModelListener#treeNodesRemoved(javax.swing.event.TreeModelEvent)
		 */
		public void treeNodesRemoved(final TreeModelEvent e) {
			// TODO Auto-generated method stub

		}

		/*
		 * (non-Javadoc)
		 *
		 * @see javax.swing.event.TreeModelListener#treeStructureChanged(javax.swing.event.TreeModelEvent)
		 */
		public void treeStructureChanged(final TreeModelEvent e) {
			// TODO Auto-generated method stub

		}
	}

}
